// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/log.h"

#include <cstdarg>
#include <memory>
#include <sstream>

#include "src/api-inl.h"
#include "src/bailout-reason.h"
#include "src/base/platform/platform.h"
#include "src/bootstrapper.h"
#include "src/counters.h"
#include "src/deoptimizer.h"
#include "src/global-handles.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate.h"
#include "src/libsampler/sampler.h"
#include "src/log-inl.h"
#include "src/log-utils.h"
#include "src/macro-assembler.h"
#include "src/memcopy.h"
#include "src/objects/api-callbacks.h"
#include "src/perf-jit.h"
#include "src/profiler/tick-sample.h"
#include "src/runtime-profiler.h"
#include "src/snapshot/embedded-data.h"
#include "src/source-position-table.h"
#include "src/string-stream.h"
#include "src/tracing/tracing-category-observer.h"
#include "src/unicode-inl.h"
#include "src/vm-state-inl.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-objects-inl.h"

#include "src/version.h"

namespace v8 {
namespace internal {

#define DECLARE_EVENT(ignore1, name) #name,
    static const char* kLogEventsNames[CodeEventListener::NUMBER_OF_LOG_EVENTS] = {
        LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)
    };
#undef DECLARE_EVENT

    static v8::CodeEventType GetCodeEventTypeForTag(
        CodeEventListener::LogEventsAndTags tag)
    {
        switch (tag) {
        case CodeEventListener::NUMBER_OF_LOG_EVENTS:
#define V(Event, _) case CodeEventListener::Event:
            LOG_EVENTS_LIST(V)
#undef V
            return v8::CodeEventType::kUnknownType;
#define V(From, To)               \
    case CodeEventListener::From: \
        return v8::CodeEventType::k##To##Type;
            TAGS_LIST(V)
#undef V
        }
        // The execution should never pass here
        UNREACHABLE();
        // NOTE(mmarchini): Workaround to fix a compiler failure on GCC 4.9
        return v8::CodeEventType::kUnknownType;
    }
#define CALL_CODE_EVENT_HANDLER(Call) \
    if (listener_) {                  \
        listener_->Call;              \
    } else {                          \
        PROFILE(isolate_, Call);      \
    }

    static const char* ComputeMarker(SharedFunctionInfo shared, AbstractCode code)
    {
        switch (code->kind()) {
        case AbstractCode::INTERPRETED_FUNCTION:
            return shared->optimization_disabled() ? "" : "~";
        case AbstractCode::OPTIMIZED_FUNCTION:
            return "*";
        default:
            return "";
        }
    }

    static const char* ComputeMarker(const wasm::WasmCode* code)
    {
        switch (code->kind()) {
        case wasm::WasmCode::kFunction:
            return code->is_liftoff() ? "" : "*";
        case wasm::WasmCode::kInterpreterEntry:
            return "~";
        default:
            return "";
        }
    }

    class CodeEventLogger::NameBuffer {
    public:
        NameBuffer() { Reset(); }

        void Reset()
        {
            utf8_pos_ = 0;
        }

        void Init(CodeEventListener::LogEventsAndTags tag)
        {
            Reset();
            AppendBytes(kLogEventsNames[tag]);
            AppendByte(':');
        }

        void AppendName(Name name)
        {
            if (name->IsString()) {
                AppendString(String::cast(name));
            } else {
                Symbol symbol = Symbol::cast(name);
                AppendBytes("symbol(");
                if (!symbol->name()->IsUndefined()) {
                    AppendBytes("\"");
                    AppendString(String::cast(symbol->name()));
                    AppendBytes("\" ");
                }
                AppendBytes("hash ");
                AppendHex(symbol->Hash());
                AppendByte(')');
            }
        }

        void AppendString(String str)
        {
            if (str.is_null())
                return;
            int length = 0;
            std::unique_ptr<char[]> c_str = str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, &length);
            AppendBytes(c_str.get(), length);
        }

        void AppendBytes(const char* bytes, int size)
        {
            size = Min(size, kUtf8BufferSize - utf8_pos_);
            MemCopy(utf8_buffer_ + utf8_pos_, bytes, size);
            utf8_pos_ += size;
        }

        void AppendBytes(const char* bytes)
        {
            AppendBytes(bytes, StrLength(bytes));
        }

        void AppendByte(char c)
        {
            if (utf8_pos_ >= kUtf8BufferSize)
                return;
            utf8_buffer_[utf8_pos_++] = c;
        }

        void AppendInt(int n)
        {
            int space = kUtf8BufferSize - utf8_pos_;
            if (space <= 0)
                return;
            Vector<char> buffer(utf8_buffer_ + utf8_pos_, space);
            int size = SNPrintF(buffer, "%d", n);
            if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
                utf8_pos_ += size;
            }
        }

        void AppendHex(uint32_t n)
        {
            int space = kUtf8BufferSize - utf8_pos_;
            if (space <= 0)
                return;
            Vector<char> buffer(utf8_buffer_ + utf8_pos_, space);
            int size = SNPrintF(buffer, "%x", n);
            if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
                utf8_pos_ += size;
            }
        }

        const char* get() { return utf8_buffer_; }
        int size() const { return utf8_pos_; }

    private:
        static const int kUtf8BufferSize = 512;
        static const int kUtf16BufferSize = kUtf8BufferSize;

        int utf8_pos_;
        char utf8_buffer_[kUtf8BufferSize];
    };

    CodeEventLogger::CodeEventLogger(Isolate* isolate)
        : isolate_(isolate)
        , name_buffer_(new NameBuffer)
    {
    }

    CodeEventLogger::~CodeEventLogger() { delete name_buffer_; }

    void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code, const char* comment)
    {
        name_buffer_->Init(tag);
        name_buffer_->AppendBytes(comment);
        LogRecordedBuffer(code, SharedFunctionInfo(), name_buffer_->get(),
            name_buffer_->size());
    }

    void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code, Name name)
    {
        name_buffer_->Init(tag);
        name_buffer_->AppendName(name);
        LogRecordedBuffer(code, SharedFunctionInfo(), name_buffer_->get(),
            name_buffer_->size());
    }

    void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code,
        SharedFunctionInfo shared, Name name)
    {
        name_buffer_->Init(tag);
        name_buffer_->AppendBytes(ComputeMarker(shared, code));
        name_buffer_->AppendByte(' ');
        name_buffer_->AppendName(name);
        LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size());
    }

    void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code,
        SharedFunctionInfo shared, Name source,
        int line, int column)
    {
        name_buffer_->Init(tag);
        name_buffer_->AppendBytes(ComputeMarker(shared, code));
        name_buffer_->AppendString(shared->DebugName());
        name_buffer_->AppendByte(' ');
        if (source->IsString()) {
            name_buffer_->AppendString(String::cast(source));
        } else {
            name_buffer_->AppendBytes("symbol(hash ");
            name_buffer_->AppendHex(Name::cast(source)->Hash());
            name_buffer_->AppendByte(')');
        }
        name_buffer_->AppendByte(':');
        name_buffer_->AppendInt(line);
        LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size());
    }

    void CodeEventLogger::CodeCreateEvent(LogEventsAndTags tag,
        const wasm::WasmCode* code,
        wasm::WasmName name)
    {
        name_buffer_->Init(tag);
        if (name.empty()) {
            name_buffer_->AppendBytes("<wasm-unknown>");
        } else {
            name_buffer_->AppendBytes(name.start(), name.length());
        }
        name_buffer_->AppendByte('-');
        if (code->IsAnonymous()) {
            name_buffer_->AppendBytes("<anonymous>");
        } else {
            name_buffer_->AppendInt(code->index());
        }
        LogRecordedBuffer(code, name_buffer_->get(), name_buffer_->size());
    }

    void CodeEventLogger::RegExpCodeCreateEvent(AbstractCode code, String source)
    {
        name_buffer_->Init(CodeEventListener::REG_EXP_TAG);
        name_buffer_->AppendString(source);
        LogRecordedBuffer(code, SharedFunctionInfo(), name_buffer_->get(),
            name_buffer_->size());
    }

    // Linux perf tool logging support
    class PerfBasicLogger : public CodeEventLogger {
    public:
        explicit PerfBasicLogger(Isolate* isolate);
        ~PerfBasicLogger() override;

        void CodeMoveEvent(AbstractCode from, AbstractCode to) override { }
        void CodeDisableOptEvent(AbstractCode code,
            SharedFunctionInfo shared) override { }

    private:
        void LogRecordedBuffer(AbstractCode code, SharedFunctionInfo shared,
            const char* name, int length) override;
        void LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
            int length) override;
        void WriteLogRecordedBuffer(uintptr_t address, int size, const char* name,
            int name_length);

        // Extension added to V8 log file name to get the low-level log name.
        static const char kFilenameFormatString[];
        static const int kFilenameBufferPadding;

        FILE* perf_output_handle_;
    };

    const char PerfBasicLogger::kFilenameFormatString[] = "/tmp/perf-%d.map";
    // Extra space for the PID in the filename
    const int PerfBasicLogger::kFilenameBufferPadding = 16;

    PerfBasicLogger::PerfBasicLogger(Isolate* isolate)
        : CodeEventLogger(isolate)
        , perf_output_handle_(nullptr)
    {
        // Open the perf JIT dump file.
        int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
        ScopedVector<char> perf_dump_name(bufferSize);
        int size = SNPrintF(
            perf_dump_name,
            kFilenameFormatString,
            base::OS::GetCurrentProcessId());
        CHECK_NE(size, -1);
        perf_output_handle_ = base::OS::FOpen(perf_dump_name.start(), base::OS::LogFileOpenMode);
        CHECK_NOT_NULL(perf_output_handle_);
        setvbuf(perf_output_handle_, nullptr, _IOLBF, 0);
    }

    PerfBasicLogger::~PerfBasicLogger()
    {
        fclose(perf_output_handle_);
        perf_output_handle_ = nullptr;
    }

    void PerfBasicLogger::WriteLogRecordedBuffer(uintptr_t address, int size,
        const char* name,
        int name_length)
    {
        // Linux perf expects hex literals without a leading 0x, while some
        // implementations of printf might prepend one when using the %p format
        // for pointers, leading to wrongly formatted JIT symbols maps.
        //
        // Instead, we use V8PRIxPTR format string and cast pointer to uintpr_t,
        // so that we have control over the exact output format.
        base::OS::FPrint(perf_output_handle_, "%" V8PRIxPTR " %x %.*s\n", address,
            size, name_length, name);
    }

    void PerfBasicLogger::LogRecordedBuffer(AbstractCode code, SharedFunctionInfo,
        const char* name, int length)
    {
        if (FLAG_perf_basic_prof_only_functions && (code->kind() != AbstractCode::INTERPRETED_FUNCTION && code->kind() != AbstractCode::BUILTIN && code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) {
            return;
        }

        WriteLogRecordedBuffer(static_cast<uintptr_t>(code->InstructionStart()),
            code->InstructionSize(), name, length);
    }

    void PerfBasicLogger::LogRecordedBuffer(const wasm::WasmCode* code,
        const char* name, int length)
    {
        WriteLogRecordedBuffer(static_cast<uintptr_t>(code->instruction_start()),
            code->instructions().length(), name, length);
    }

    // External CodeEventListener
    ExternalCodeEventListener::ExternalCodeEventListener(Isolate* isolate)
        : is_listening_(false)
        , isolate_(isolate)
        , code_event_handler_(nullptr)
    {
    }

    ExternalCodeEventListener::~ExternalCodeEventListener()
    {
        if (is_listening_) {
            StopListening();
        }
    }

    void ExternalCodeEventListener::LogExistingCode()
    {
        HandleScope scope(isolate_);
        ExistingCodeLogger logger(isolate_, this);
        logger.LogCodeObjects();
        logger.LogCompiledFunctions();
    }

    void ExternalCodeEventListener::StartListening(
        CodeEventHandler* code_event_handler)
    {
        if (is_listening_ || code_event_handler == nullptr) {
            return;
        }
        code_event_handler_ = code_event_handler;
        is_listening_ = isolate_->code_event_dispatcher()->AddListener(this);
        if (is_listening_) {
            LogExistingCode();
        }
    }

    void ExternalCodeEventListener::StopListening()
    {
        if (!is_listening_) {
            return;
        }

        isolate_->code_event_dispatcher()->RemoveListener(this);
        is_listening_ = false;
    }

    void ExternalCodeEventListener::CodeCreateEvent(
        CodeEventListener::LogEventsAndTags tag, AbstractCode code,
        const char* comment)
    {
        CodeEvent code_event;
        code_event.code_start_address = static_cast<uintptr_t>(code->InstructionStart());
        code_event.code_size = static_cast<size_t>(code->InstructionSize());
        code_event.function_name = isolate_->factory()->empty_string();
        code_event.script_name = isolate_->factory()->empty_string();
        code_event.script_line = 0;
        code_event.script_column = 0;
        code_event.code_type = GetCodeEventTypeForTag(tag);
        code_event.comment = comment;

        code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
    }

    void ExternalCodeEventListener::CodeCreateEvent(
        CodeEventListener::LogEventsAndTags tag, AbstractCode code, Name name)
    {
        Handle<String> name_string = Name::ToFunctionName(isolate_, Handle<Name>(name, isolate_))
                                         .ToHandleChecked();

        CodeEvent code_event;
        code_event.code_start_address = static_cast<uintptr_t>(code->InstructionStart());
        code_event.code_size = static_cast<size_t>(code->InstructionSize());
        code_event.function_name = name_string;
        code_event.script_name = isolate_->factory()->empty_string();
        code_event.script_line = 0;
        code_event.script_column = 0;
        code_event.code_type = GetCodeEventTypeForTag(tag);
        code_event.comment = "";

        code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
    }

    void ExternalCodeEventListener::CodeCreateEvent(
        CodeEventListener::LogEventsAndTags tag, AbstractCode code,
        SharedFunctionInfo shared, Name name)
    {
        Handle<String> name_string = Name::ToFunctionName(isolate_, Handle<Name>(name, isolate_))
                                         .ToHandleChecked();

        CodeEvent code_event;
        code_event.code_start_address = static_cast<uintptr_t>(code->InstructionStart());
        code_event.code_size = static_cast<size_t>(code->InstructionSize());
        code_event.function_name = name_string;
        code_event.script_name = isolate_->factory()->empty_string();
        code_event.script_line = 0;
        code_event.script_column = 0;
        code_event.code_type = GetCodeEventTypeForTag(tag);
        code_event.comment = "";

        code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
    }

    void ExternalCodeEventListener::CodeCreateEvent(
        CodeEventListener::LogEventsAndTags tag, AbstractCode code,
        SharedFunctionInfo shared, Name source, int line, int column)
    {
        Handle<String> name_string = Name::ToFunctionName(isolate_, Handle<Name>(shared->Name(), isolate_))
                                         .ToHandleChecked();
        Handle<String> source_string = Name::ToFunctionName(isolate_, Handle<Name>(source, isolate_))
                                           .ToHandleChecked();

        CodeEvent code_event;
        code_event.code_start_address = static_cast<uintptr_t>(code->InstructionStart());
        code_event.code_size = static_cast<size_t>(code->InstructionSize());
        code_event.function_name = name_string;
        code_event.script_name = source_string;
        code_event.script_line = line;
        code_event.script_column = column;
        code_event.code_type = GetCodeEventTypeForTag(tag);
        code_event.comment = "";

        code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
    }

    void ExternalCodeEventListener::CodeCreateEvent(LogEventsAndTags tag,
        const wasm::WasmCode* code,
        wasm::WasmName name)
    {
        // TODO(mmarchini): handle later
    }

    void ExternalCodeEventListener::RegExpCodeCreateEvent(AbstractCode code,
        String source)
    {
        CodeEvent code_event;
        code_event.code_start_address = static_cast<uintptr_t>(code->InstructionStart());
        code_event.code_size = static_cast<size_t>(code->InstructionSize());
        code_event.function_name = Handle<String>(source, isolate_);
        code_event.script_name = isolate_->factory()->empty_string();
        code_event.script_line = 0;
        code_event.script_column = 0;
        code_event.code_type = GetCodeEventTypeForTag(CodeEventListener::REG_EXP_TAG);
        code_event.comment = "";

        code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
    }

    // Low-level logging support.
    class LowLevelLogger : public CodeEventLogger {
    public:
        LowLevelLogger(Isolate* isolate, const char* file_name);
        ~LowLevelLogger() override;

        void CodeMoveEvent(AbstractCode from, AbstractCode to) override;
        void CodeDisableOptEvent(AbstractCode code,
            SharedFunctionInfo shared) override { }
        void SnapshotPositionEvent(HeapObject obj, int pos);
        void CodeMovingGCEvent() override;

    private:
        void LogRecordedBuffer(AbstractCode code, SharedFunctionInfo shared,
            const char* name, int length) override;
        void LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
            int length) override;

        // Low-level profiling event structures.
        struct CodeCreateStruct {
            static const char kTag = 'C';

            int32_t name_size;
            Address code_address;
            int32_t code_size;
        };

        struct CodeMoveStruct {
            static const char kTag = 'M';

            Address from_address;
            Address to_address;
        };

        static const char kCodeMovingGCTag = 'G';

        // Extension added to V8 log file name to get the low-level log name.
        static const char kLogExt[];

        void LogCodeInfo();
        void LogWriteBytes(const char* bytes, int size);

        template <typename T>
        void LogWriteStruct(const T& s)
        {
            char tag = T::kTag;
            LogWriteBytes(reinterpret_cast<const char*>(&tag), sizeof(tag));
            LogWriteBytes(reinterpret_cast<const char*>(&s), sizeof(s));
        }

        FILE* ll_output_handle_;
    };

    const char LowLevelLogger::kLogExt[] = ".ll";

    LowLevelLogger::LowLevelLogger(Isolate* isolate, const char* name)
        : CodeEventLogger(isolate)
        , ll_output_handle_(nullptr)
    {
        // Open the low-level log file.
        size_t len = strlen(name);
        ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLogExt)));
        MemCopy(ll_name.start(), name, len);
        MemCopy(ll_name.start() + len, kLogExt, sizeof(kLogExt));
        ll_output_handle_ = base::OS::FOpen(ll_name.start(), base::OS::LogFileOpenMode);
        setvbuf(ll_output_handle_, nullptr, _IOLBF, 0);

        LogCodeInfo();
    }

    LowLevelLogger::~LowLevelLogger()
    {
        fclose(ll_output_handle_);
        ll_output_handle_ = nullptr;
    }

    void LowLevelLogger::LogCodeInfo()
    {
#if V8_TARGET_ARCH_IA32
        const char arch[] = "ia32";
#elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT
        const char arch[] = "x64";
#elif V8_TARGET_ARCH_ARM
        const char arch[] = "arm";
#elif V8_TARGET_ARCH_PPC
        const char arch[] = "ppc";
#elif V8_TARGET_ARCH_MIPS
        const char arch[] = "mips";
#elif V8_TARGET_ARCH_ARM64
        const char arch[] = "arm64";
#elif V8_TARGET_ARCH_S390
        const char arch[] = "s390";
#else
        const char arch[] = "unknown";
#endif
        LogWriteBytes(arch, sizeof(arch));
    }

    void LowLevelLogger::LogRecordedBuffer(AbstractCode code, SharedFunctionInfo,
        const char* name, int length)
    {
        CodeCreateStruct event;
        event.name_size = length;
        event.code_address = code->InstructionStart();
        event.code_size = code->InstructionSize();
        LogWriteStruct(event);
        LogWriteBytes(name, length);
        LogWriteBytes(reinterpret_cast<const char*>(code->InstructionStart()),
            code->InstructionSize());
    }

    void LowLevelLogger::LogRecordedBuffer(const wasm::WasmCode* code,
        const char* name, int length)
    {
        CodeCreateStruct event;
        event.name_size = length;
        event.code_address = code->instruction_start();
        event.code_size = code->instructions().length();
        LogWriteStruct(event);
        LogWriteBytes(name, length);
        LogWriteBytes(reinterpret_cast<const char*>(code->instruction_start()),
            code->instructions().length());
    }

    void LowLevelLogger::CodeMoveEvent(AbstractCode from, AbstractCode to)
    {
        CodeMoveStruct event;
        event.from_address = from->InstructionStart();
        event.to_address = to->InstructionStart();
        LogWriteStruct(event);
    }

    void LowLevelLogger::LogWriteBytes(const char* bytes, int size)
    {
        size_t rv = fwrite(bytes, 1, size, ll_output_handle_);
        DCHECK(static_cast<size_t>(size) == rv);
        USE(rv);
    }

    void LowLevelLogger::CodeMovingGCEvent()
    {
        const char tag = kCodeMovingGCTag;

        LogWriteBytes(&tag, sizeof(tag));
    }

    class JitLogger : public CodeEventLogger {
    public:
        JitLogger(Isolate* isolate, JitCodeEventHandler code_event_handler);

        void CodeMoveEvent(AbstractCode from, AbstractCode to) override;
        void CodeDisableOptEvent(AbstractCode code,
            SharedFunctionInfo shared) override { }
        void AddCodeLinePosInfoEvent(void* jit_handler_data, int pc_offset,
            int position,
            JitCodeEvent::PositionType position_type);

        void* StartCodePosInfoEvent();
        void EndCodePosInfoEvent(Address start_address, void* jit_handler_data);

    private:
        void LogRecordedBuffer(AbstractCode code, SharedFunctionInfo shared,
            const char* name, int length) override;
        void LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
            int length) override;

        JitCodeEventHandler code_event_handler_;
        base::Mutex logger_mutex_;
    };

    JitLogger::JitLogger(Isolate* isolate, JitCodeEventHandler code_event_handler)
        : CodeEventLogger(isolate)
        , code_event_handler_(code_event_handler)
    {
    }

    void JitLogger::LogRecordedBuffer(AbstractCode code, SharedFunctionInfo shared,
        const char* name, int length)
    {
        JitCodeEvent event;
        memset(static_cast<void*>(&event), 0, sizeof(event));
        event.type = JitCodeEvent::CODE_ADDED;
        event.code_start = reinterpret_cast<void*>(code->InstructionStart());
        event.code_type = code->IsCode() ? JitCodeEvent::JIT_CODE : JitCodeEvent::BYTE_CODE;
        event.code_len = code->InstructionSize();
        Handle<SharedFunctionInfo> shared_function_handle;
        if (!shared.is_null() && shared->script()->IsScript()) {
            shared_function_handle = Handle<SharedFunctionInfo>(shared, shared->GetIsolate());
        }
        event.script = ToApiHandle<v8::UnboundScript>(shared_function_handle);
        event.name.str = name;
        event.name.len = length;
        event.isolate = reinterpret_cast<v8::Isolate*>(isolate_);
        code_event_handler_(&event);
    }

    void JitLogger::LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
        int length)
    {
        JitCodeEvent event;
        memset(static_cast<void*>(&event), 0, sizeof(event));
        event.type = JitCodeEvent::CODE_ADDED;
        event.code_type = JitCodeEvent::JIT_CODE;
        event.code_start = code->instructions().start();
        event.code_len = code->instructions().length();
        event.name.str = name;
        event.name.len = length;
        event.isolate = reinterpret_cast<v8::Isolate*>(isolate_);
        code_event_handler_(&event);
    }

    void JitLogger::CodeMoveEvent(AbstractCode from, AbstractCode to)
    {
        base::MutexGuard guard(&logger_mutex_);

        JitCodeEvent event;
        event.type = JitCodeEvent::CODE_MOVED;
        event.code_type = from->IsCode() ? JitCodeEvent::JIT_CODE : JitCodeEvent::BYTE_CODE;
        event.code_start = reinterpret_cast<void*>(from->InstructionStart());
        event.code_len = from->InstructionSize();
        event.new_code_start = reinterpret_cast<void*>(to->InstructionStart());
        event.isolate = reinterpret_cast<v8::Isolate*>(isolate_);

        code_event_handler_(&event);
    }

    void JitLogger::AddCodeLinePosInfoEvent(
        void* jit_handler_data,
        int pc_offset,
        int position,
        JitCodeEvent::PositionType position_type)
    {
        JitCodeEvent event;
        memset(static_cast<void*>(&event), 0, sizeof(event));
        event.type = JitCodeEvent::CODE_ADD_LINE_POS_INFO;
        event.user_data = jit_handler_data;
        event.line_info.offset = pc_offset;
        event.line_info.pos = position;
        event.line_info.position_type = position_type;
        event.isolate = reinterpret_cast<v8::Isolate*>(isolate_);

        code_event_handler_(&event);
    }

    void* JitLogger::StartCodePosInfoEvent()
    {
        JitCodeEvent event;
        memset(static_cast<void*>(&event), 0, sizeof(event));
        event.type = JitCodeEvent::CODE_START_LINE_INFO_RECORDING;
        event.isolate = reinterpret_cast<v8::Isolate*>(isolate_);

        code_event_handler_(&event);
        return event.user_data;
    }

    void JitLogger::EndCodePosInfoEvent(Address start_address,
        void* jit_handler_data)
    {
        JitCodeEvent event;
        memset(static_cast<void*>(&event), 0, sizeof(event));
        event.type = JitCodeEvent::CODE_END_LINE_INFO_RECORDING;
        event.code_start = reinterpret_cast<void*>(start_address);
        event.user_data = jit_handler_data;
        event.isolate = reinterpret_cast<v8::Isolate*>(isolate_);

        code_event_handler_(&event);
    }

    // TODO(lpy): Keeping sampling thread inside V8 is a workaround currently,
    // the reason is to reduce code duplication during migration to sampler library,
    // sampling thread, as well as the sampler, will be moved to D8 eventually.
    class SamplingThread : public base::Thread {
    public:
        static const int kSamplingThreadStackSize = 64 * KB;

        SamplingThread(sampler::Sampler* sampler, int interval_microseconds)
            : base::Thread(
                base::Thread::Options("SamplingThread", kSamplingThreadStackSize))
            , sampler_(sampler)
            , interval_microseconds_(interval_microseconds)
        {
        }
        void Run() override
        {
            while (sampler_->IsActive()) {
                sampler_->DoSample();
                base::OS::Sleep(
                    base::TimeDelta::FromMicroseconds(interval_microseconds_));
            }
        }

    private:
        sampler::Sampler* sampler_;
        const int interval_microseconds_;
    };

    // The Profiler samples pc and sp values for the main thread.
    // Each sample is appended to a circular buffer.
    // An independent thread removes data and writes it to the log.
    // This design minimizes the time spent in the sampler.
    //
    class Profiler : public base::Thread {
    public:
        explicit Profiler(Isolate* isolate);
        void Engage();
        void Disengage();

        // Inserts collected profiling data into buffer.
        void Insert(v8::TickSample* sample)
        {
            if (Succ(head_) == static_cast<int>(base::Relaxed_Load(&tail_))) {
                overflow_ = true;
            } else {
                buffer_[head_] = *sample;
                head_ = Succ(head_);
                buffer_semaphore_.Signal(); // Tell we have an element.
            }
        }

        void Run() override;

    private:
        // Waits for a signal and removes profiling data.
        bool Remove(v8::TickSample* sample)
        {
            buffer_semaphore_.Wait(); // Wait for an element.
            *sample = buffer_[base::Relaxed_Load(&tail_)];
            bool result = overflow_;
            base::Relaxed_Store(
                &tail_, static_cast<base::Atomic32>(Succ(base::Relaxed_Load(&tail_))));
            overflow_ = false;
            return result;
        }

        // Returns the next index in the cyclic buffer.
        int Succ(int index) { return (index + 1) % kBufferSize; }

        Isolate* isolate_;
        // Cyclic buffer for communicating profiling samples
        // between the signal handler and the worker thread.
        static const int kBufferSize = 128;
        v8::TickSample buffer_[kBufferSize]; // Buffer storage.
        int head_; // Index to the buffer head.
        base::Atomic32 tail_; // Index to the buffer tail.
        bool overflow_; // Tell whether a buffer overflow has occurred.
        // Semaphore used for buffer synchronization.
        base::Semaphore buffer_semaphore_;

        // Tells whether worker thread should continue running.
        base::Atomic32 running_;
    };

    //
    // Ticker used to provide ticks to the profiler and the sliding state
    // window.
    //
    class Ticker : public sampler::Sampler {
    public:
        Ticker(Isolate* isolate, int interval_microseconds)
            : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate))
            , sampling_thread_(
                  base::make_unique<SamplingThread>(this, interval_microseconds))
        {
        }

        ~Ticker() override
        {
            if (IsActive())
                Stop();
        }

        void SetProfiler(Profiler* profiler)
        {
            DCHECK_NULL(profiler_);
            profiler_ = profiler;
            if (!IsActive())
                Start();
            sampling_thread_->StartSynchronously();
        }

        void ClearProfiler()
        {
            profiler_ = nullptr;
            if (IsActive())
                Stop();
            sampling_thread_->Join();
        }

        void SampleStack(const v8::RegisterState& state) override
        {
            if (!profiler_)
                return;
            Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
            TickSample sample;
            sample.Init(isolate, state, TickSample::kIncludeCEntryFrame, true);
            profiler_->Insert(&sample);
        }

    private:
        Profiler* profiler_ = nullptr;
        std::unique_ptr<SamplingThread> sampling_thread_;
    };

    //
    // Profiler implementation when invoking with --prof.
    //
    Profiler::Profiler(Isolate* isolate)
        : base::Thread(Options("v8:Profiler"))
        , isolate_(isolate)
        , head_(0)
        , overflow_(false)
        , buffer_semaphore_(0)
    {
        base::Relaxed_Store(&tail_, 0);
        base::Relaxed_Store(&running_, 0);
    }

    void Profiler::Engage()
    {
        std::vector<base::OS::SharedLibraryAddress> addresses = base::OS::GetSharedLibraryAddresses();
        for (const auto& address : addresses) {
            LOG(isolate_, SharedLibraryEvent(address.library_path, address.start, address.end, address.aslr_slide));
        }

        // Start thread processing the profiler buffer.
        base::Relaxed_Store(&running_, 1);
        Start();

        // Register to get ticks.
        Logger* logger = isolate_->logger();
        logger->ticker_->SetProfiler(this);

        logger->ProfilerBeginEvent();
    }

    void Profiler::Disengage()
    {
        // Stop receiving ticks.
        isolate_->logger()->ticker_->ClearProfiler();

        // Terminate the worker thread by setting running_ to false,
        // inserting a fake element in the queue and then wait for
        // the thread to terminate.
        base::Relaxed_Store(&running_, 0);
        v8::TickSample sample;
        Insert(&sample);
        Join();

        LOG(isolate_, UncheckedStringEvent("profiler", "end"));
    }

    void Profiler::Run()
    {
        v8::TickSample sample;
        bool overflow = Remove(&sample);
        while (base::Relaxed_Load(&running_)) {
            LOG(isolate_, TickEvent(&sample, overflow));
            overflow = Remove(&sample);
        }
    }

    //
    // Logger class implementation.
    //

    Logger::Logger(Isolate* isolate)
        : isolate_(isolate)
        , log_events_(nullptr)
        , is_logging_(false)
        , log_(nullptr)
        , is_initialized_(false)
        , existing_code_logger_(isolate)
    {
    }

    Logger::~Logger()
    {
        delete log_;
    }

    const LogSeparator Logger::kNext = LogSeparator::kSeparator;

    void Logger::AddCodeEventListener(CodeEventListener* listener)
    {
        bool result = isolate_->code_event_dispatcher()->AddListener(listener);
        CHECK(result);
    }

    void Logger::RemoveCodeEventListener(CodeEventListener* listener)
    {
        isolate_->code_event_dispatcher()->RemoveListener(listener);
    }

    void Logger::ProfilerBeginEvent()
    {
        if (!log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        msg << "profiler" << kNext << "begin" << kNext << FLAG_prof_sampling_interval;
        msg.WriteToLogFile();
    }

    void Logger::StringEvent(const char* name, const char* value)
    {
        if (FLAG_log)
            UncheckedStringEvent(name, value);
    }

    void Logger::UncheckedStringEvent(const char* name, const char* value)
    {
        if (!log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        msg << name << kNext << value;
        msg.WriteToLogFile();
    }

    void Logger::IntPtrTEvent(const char* name, intptr_t value)
    {
        if (FLAG_log)
            UncheckedIntPtrTEvent(name, value);
    }

    void Logger::UncheckedIntPtrTEvent(const char* name, intptr_t value)
    {
        if (!log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        msg << name << kNext;
        msg.AppendFormatString("%" V8PRIdPTR, value);
        msg.WriteToLogFile();
    }

    void Logger::HandleEvent(const char* name, Address* location)
    {
        if (!log_->IsEnabled() || !FLAG_log_handles)
            return;
        Log::MessageBuilder msg(log_);
        msg << name << kNext << reinterpret_cast<void*>(location);
        msg.WriteToLogFile();
    }

    void Logger::ApiSecurityCheck()
    {
        if (!log_->IsEnabled() || !FLAG_log_api)
            return;
        Log::MessageBuilder msg(log_);
        msg << "api" << kNext << "check-security";
        msg.WriteToLogFile();
    }

    void Logger::SharedLibraryEvent(const std::string& library_path,
        uintptr_t start, uintptr_t end,
        intptr_t aslr_slide)
    {
        if (!log_->IsEnabled() || !FLAG_prof_cpp)
            return;
        Log::MessageBuilder msg(log_);
        msg << "shared-library" << kNext << library_path.c_str() << kNext
            << reinterpret_cast<void*>(start) << kNext << reinterpret_cast<void*>(end)
            << kNext << aslr_slide;
        msg.WriteToLogFile();
    }

    void Logger::CodeDeoptEvent(Code code, DeoptimizeKind kind, Address pc,
        int fp_to_sp_delta)
    {
        if (!log_->IsEnabled())
            return;
        Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
        Log::MessageBuilder msg(log_);
        msg << "code-deopt" << kNext << timer_.Elapsed().InMicroseconds() << kNext
            << code->CodeSize() << kNext
            << reinterpret_cast<void*>(code->InstructionStart());

        // Deoptimization position.
        std::ostringstream deopt_location;
        int inlining_id = -1;
        int script_offset = -1;
        if (info.position.IsKnown()) {
            info.position.Print(deopt_location, code);
            inlining_id = info.position.InliningId();
            script_offset = info.position.ScriptOffset();
        } else {
            deopt_location << "<unknown>";
        }
        msg << kNext << inlining_id << kNext << script_offset << kNext;
        msg << Deoptimizer::MessageFor(kind) << kNext;
        msg << deopt_location.str().c_str() << kNext
            << DeoptimizeReasonToString(info.deopt_reason);
        msg.WriteToLogFile();
    }

    void Logger::CurrentTimeEvent()
    {
        if (!log_->IsEnabled())
            return;
        DCHECK(FLAG_log_internal_timer_events);
        Log::MessageBuilder msg(log_);
        msg << "current-time" << kNext << timer_.Elapsed().InMicroseconds();
        msg.WriteToLogFile();
    }

    void Logger::TimerEvent(Logger::StartEnd se, const char* name)
    {
        if (!log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        switch (se) {
        case START:
            msg << "timer-event-start";
            break;
        case END:
            msg << "timer-event-end";
            break;
        case STAMP:
            msg << "timer-event";
        }
        msg << kNext << name << kNext << timer_.Elapsed().InMicroseconds();
        msg.WriteToLogFile();
    }

    // static
    void Logger::EnterExternal(Isolate* isolate)
    {
        DCHECK(FLAG_log_internal_timer_events);
        LOG(isolate, TimerEvent(START, TimerEventExternal::name()));
        DCHECK(isolate->current_vm_state() == JS);
        isolate->set_current_vm_state(EXTERNAL);
    }

    // static
    void Logger::LeaveExternal(Isolate* isolate)
    {
        DCHECK(FLAG_log_internal_timer_events);
        LOG(isolate, TimerEvent(END, TimerEventExternal::name()));
        DCHECK(isolate->current_vm_state() == EXTERNAL);
        isolate->set_current_vm_state(JS);
    }

// Instantiate template methods.
#define V(TimerName, expose)                                             \
    template void TimerEventScope<TimerEvent##TimerName>::LogTimerEvent( \
        Logger::StartEnd se);
    TIMER_EVENTS_LIST(V)
#undef V

    void Logger::ApiNamedPropertyAccess(const char* tag, JSObject holder,
        Object property_name)
    {
        DCHECK(property_name->IsName());
        if (!log_->IsEnabled() || !FLAG_log_api)
            return;
        Log::MessageBuilder msg(log_);
        msg << "api" << kNext << tag << kNext << holder->class_name() << kNext
            << Name::cast(property_name);
        msg.WriteToLogFile();
    }

    void Logger::ApiIndexedPropertyAccess(const char* tag, JSObject holder,
        uint32_t index)
    {
        if (!log_->IsEnabled() || !FLAG_log_api)
            return;
        Log::MessageBuilder msg(log_);
        msg << "api" << kNext << tag << kNext << holder->class_name() << kNext
            << index;
        msg.WriteToLogFile();
    }

    void Logger::ApiObjectAccess(const char* tag, JSObject object)
    {
        if (!log_->IsEnabled() || !FLAG_log_api)
            return;
        Log::MessageBuilder msg(log_);
        msg << "api" << kNext << tag << kNext << object->class_name();
        msg.WriteToLogFile();
    }

    void Logger::ApiEntryCall(const char* name)
    {
        if (!log_->IsEnabled() || !FLAG_log_api)
            return;
        Log::MessageBuilder msg(log_);
        msg << "api" << kNext << name;
        msg.WriteToLogFile();
    }

    void Logger::NewEvent(const char* name, void* object, size_t size)
    {
        if (!log_->IsEnabled() || !FLAG_log)
            return;
        Log::MessageBuilder msg(log_);
        msg << "new" << kNext << name << kNext << object << kNext
            << static_cast<unsigned int>(size);
        msg.WriteToLogFile();
    }

    void Logger::DeleteEvent(const char* name, void* object)
    {
        if (!log_->IsEnabled() || !FLAG_log)
            return;
        Log::MessageBuilder msg(log_);
        msg << "delete" << kNext << name << kNext << object;
        msg.WriteToLogFile();
    }

    void Logger::CallbackEventInternal(const char* prefix, Name name,
        Address entry_point)
    {
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        msg << kLogEventsNames[CodeEventListener::CODE_CREATION_EVENT] << kNext
            << kLogEventsNames[CodeEventListener::CALLBACK_TAG] << kNext << -2
            << kNext << timer_.Elapsed().InMicroseconds() << kNext
            << reinterpret_cast<void*>(entry_point) << kNext << 1 << kNext << prefix
            << name;
        msg.WriteToLogFile();
    }

    void Logger::CallbackEvent(Name name, Address entry_point)
    {
        CallbackEventInternal("", name, entry_point);
    }

    void Logger::GetterCallbackEvent(Name name, Address entry_point)
    {
        CallbackEventInternal("get ", name, entry_point);
    }

    void Logger::SetterCallbackEvent(Name name, Address entry_point)
    {
        CallbackEventInternal("set ", name, entry_point);
    }

    namespace {

        void AppendCodeCreateHeader(Log::MessageBuilder& msg,
            CodeEventListener::LogEventsAndTags tag,
            AbstractCode::Kind kind, uint8_t* address, int size,
            base::ElapsedTimer* timer)
        {
            msg << kLogEventsNames[CodeEventListener::CODE_CREATION_EVENT]
                << Logger::kNext << kLogEventsNames[tag] << Logger::kNext << kind
                << Logger::kNext << timer->Elapsed().InMicroseconds() << Logger::kNext
                << reinterpret_cast<void*>(address) << Logger::kNext << size
                << Logger::kNext;
        }

        void AppendCodeCreateHeader(Log::MessageBuilder& msg,
            CodeEventListener::LogEventsAndTags tag,
            AbstractCode code, base::ElapsedTimer* timer)
        {
            AppendCodeCreateHeader(msg, tag, code->kind(),
                reinterpret_cast<uint8_t*>(code->InstructionStart()),
                code->InstructionSize(), timer);
        }

    } // namespace

    void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code, const char* comment)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        AppendCodeCreateHeader(msg, tag, code, &timer_);
        msg << comment;
        msg.WriteToLogFile();
    }

    void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code, Name name)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        AppendCodeCreateHeader(msg, tag, code, &timer_);
        msg << name;
        msg.WriteToLogFile();
    }

    void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code, SharedFunctionInfo shared,
        Name name)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        if (code == AbstractCode::cast(isolate_->builtins()->builtin(Builtins::kCompileLazy))) {
            return;
        }

        Log::MessageBuilder msg(log_);
        AppendCodeCreateHeader(msg, tag, code, &timer_);
        msg << name << kNext << reinterpret_cast<void*>(shared->address()) << kNext
            << ComputeMarker(shared, code);
        msg.WriteToLogFile();
    }

    void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        const wasm::WasmCode* code, wasm::WasmName name)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        AppendCodeCreateHeader(msg, tag, AbstractCode::Kind::WASM_FUNCTION,
            code->instructions().start(),
            code->instructions().length(), &timer_);
        if (name.empty()) {
            msg << "<unknown wasm>";
        } else {
            msg.AppendString(name);
        }
        // We have to add two extra fields that allow the tick processor to group
        // events for the same wasm function, even if it gets compiled again. For
        // normal JS functions, we use the shared function info. For wasm, the pointer
        // to the native module + function index works well enough.
        // TODO(herhut) Clean up the tick processor code instead.
        void* tag_ptr = reinterpret_cast<byte*>(code->native_module()) + code->index();
        msg << kNext << tag_ptr << kNext << ComputeMarker(code);
        msg.WriteToLogFile();
    }

    // Although, it is possible to extract source and line from
    // the SharedFunctionInfo object, we left it to caller
    // to leave logging functions free from heap allocations.
    void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
        AbstractCode code, SharedFunctionInfo shared,
        Name source, int line, int column)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        {
            Log::MessageBuilder msg(log_);
            AppendCodeCreateHeader(msg, tag, code, &timer_);
            msg << shared->DebugName() << " " << source << ":" << line << ":" << column
                << kNext << reinterpret_cast<void*>(shared->address()) << kNext
                << ComputeMarker(shared, code);
            msg.WriteToLogFile();
        }

        if (!FLAG_log_source_code)
            return;
        Object script_object = shared->script();
        if (!script_object->IsScript())
            return;
        Script script = Script::cast(script_object);
        if (!EnsureLogScriptSource(script))
            return;

        // We log source code information in the form:
        //
        // code-source-info <addr>,<script>,<start>,<end>,<pos>,<inline-pos>,<fns>
        //
        // where
        //   <addr> is code object address
        //   <script> is script id
        //   <start> is the starting position inside the script
        //   <end> is the end position inside the script
        //   <pos> is source position table encoded in the string,
        //      it is a sequence of C<code-offset>O<script-offset>[I<inlining-id>]
        //      where
        //        <code-offset> is the offset within the code object
        //        <script-offset> is the position within the script
        //        <inlining-id> is the offset in the <inlining> table
        //   <inlining> table is a sequence of strings of the form
        //      F<function-id>O<script-offset>[I<inlining-id>]
        //      where
        //         <function-id> is an index into the <fns> function table
        //   <fns> is the function table encoded as a sequence of strings
        //      S<shared-function-info-address>
        Log::MessageBuilder msg(log_);
        msg << "code-source-info" << kNext
            << reinterpret_cast<void*>(code->InstructionStart()) << kNext
            << script->id() << kNext << shared->StartPosition() << kNext
            << shared->EndPosition() << kNext;

        SourcePositionTableIterator iterator(code->source_position_table());
        bool hasInlined = false;
        for (; !iterator.done(); iterator.Advance()) {
            SourcePosition pos = iterator.source_position();
            msg << "C" << iterator.code_offset() << "O" << pos.ScriptOffset();
            if (pos.isInlined()) {
                msg << "I" << pos.InliningId();
                hasInlined = true;
            }
        }
        msg << kNext;
        int maxInlinedId = -1;
        if (hasInlined) {
            PodArray<InliningPosition> inlining_positions = DeoptimizationData::cast(Code::cast(code)->deoptimization_data())
                                                                ->InliningPositions();
            for (int i = 0; i < inlining_positions->length(); i++) {
                InliningPosition inlining_pos = inlining_positions->get(i);
                msg << "F";
                if (inlining_pos.inlined_function_id != -1) {
                    msg << inlining_pos.inlined_function_id;
                    if (inlining_pos.inlined_function_id > maxInlinedId) {
                        maxInlinedId = inlining_pos.inlined_function_id;
                    }
                }
                SourcePosition pos = inlining_pos.position;
                msg << "O" << pos.ScriptOffset();
                if (pos.isInlined()) {
                    msg << "I" << pos.InliningId();
                }
            }
        }
        msg << kNext;
        if (hasInlined) {
            DeoptimizationData deopt_data = DeoptimizationData::cast(Code::cast(code)->deoptimization_data());

            msg << std::hex;
            for (int i = 0; i <= maxInlinedId; i++) {
                msg << "S"
                    << reinterpret_cast<void*>(
                           deopt_data->GetInlinedFunction(i)->address());
            }
            msg << std::dec;
        }
        msg.WriteToLogFile();
    }

    void Logger::CodeDisableOptEvent(AbstractCode code, SharedFunctionInfo shared)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        msg << kLogEventsNames[CodeEventListener::CODE_DISABLE_OPT_EVENT] << kNext
            << shared->DebugName() << kNext
            << GetBailoutReason(shared->disable_optimization_reason());
        msg.WriteToLogFile();
    }

    void Logger::CodeMovingGCEvent()
    {
        if (!is_listening_to_code_events())
            return;
        if (!log_->IsEnabled() || !FLAG_ll_prof)
            return;
        base::OS::SignalCodeMovingGC();
    }

    void Logger::RegExpCodeCreateEvent(AbstractCode code, String source)
    {
        if (!is_listening_to_code_events())
            return;
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        AppendCodeCreateHeader(msg, CodeEventListener::REG_EXP_TAG, code, &timer_);
        msg << source;
        msg.WriteToLogFile();
    }

    void Logger::CodeMoveEvent(AbstractCode from, AbstractCode to)
    {
        if (!is_listening_to_code_events())
            return;
        MoveEventInternal(CodeEventListener::CODE_MOVE_EVENT, from->address(),
            to->address());
    }

    namespace {

        void CodeLinePosEvent(JitLogger* jit_logger, Address code_start,
            SourcePositionTableIterator& iter)
        {
            if (jit_logger) {
                void* jit_handler_data = jit_logger->StartCodePosInfoEvent();
                for (; !iter.done(); iter.Advance()) {
                    if (iter.is_statement()) {
                        jit_logger->AddCodeLinePosInfoEvent(
                            jit_handler_data, iter.code_offset(),
                            iter.source_position().ScriptOffset(),
                            JitCodeEvent::STATEMENT_POSITION);
                    }
                    jit_logger->AddCodeLinePosInfoEvent(jit_handler_data, iter.code_offset(),
                        iter.source_position().ScriptOffset(),
                        JitCodeEvent::POSITION);
                }
                jit_logger->EndCodePosInfoEvent(code_start, jit_handler_data);
            }
        }

    } // namespace

    void Logger::CodeLinePosInfoRecordEvent(Address code_start,
        ByteArray source_position_table)
    {
        SourcePositionTableIterator iter(source_position_table);
        CodeLinePosEvent(jit_logger_.get(), code_start, iter);
    }

    void Logger::CodeLinePosInfoRecordEvent(
        Address code_start, Vector<const byte> source_position_table)
    {
        SourcePositionTableIterator iter(source_position_table);
        CodeLinePosEvent(jit_logger_.get(), code_start, iter);
    }

    void Logger::CodeNameEvent(Address addr, int pos, const char* code_name)
    {
        if (code_name == nullptr)
            return; // Not a code object.
        Log::MessageBuilder msg(log_);
        msg << kLogEventsNames[CodeEventListener::SNAPSHOT_CODE_NAME_EVENT] << kNext
            << pos << kNext << code_name;
        msg.WriteToLogFile();
    }

    void Logger::SharedFunctionInfoMoveEvent(Address from, Address to)
    {
        if (!is_listening_to_code_events())
            return;
        MoveEventInternal(CodeEventListener::SHARED_FUNC_MOVE_EVENT, from, to);
    }

    void Logger::MoveEventInternal(CodeEventListener::LogEventsAndTags event,
        Address from, Address to)
    {
        if (!FLAG_log_code || !log_->IsEnabled())
            return;
        Log::MessageBuilder msg(log_);
        msg << kLogEventsNames[event] << kNext << reinterpret_cast<void*>(from)
            << kNext << reinterpret_cast<void*>(to);
        msg.WriteToLogFile();
    }

    void Logger::ResourceEvent(const char* name, const char* tag)
    {
        if (!log_->IsEnabled() || !FLAG_log)
            return;
        Log::MessageBuilder msg(log_);
        msg << name << kNext << tag << kNext;

        uint32_t sec, usec;
        if (base::OS::GetUserTime(&sec, &usec) != -1) {
            msg << sec << kNext << usec << kNext;
        }
        msg.AppendFormatString("%.0f",
            V8::GetCurrentPlatform()->CurrentClockTimeMillis());
        msg.WriteToLogFile();
    }

    void Logger::SuspectReadEvent(Name name, Object obj)
    {
        if (!log_->IsEnabled() || !FLAG_log_suspect)
            return;
        Log::MessageBuilder msg(log_);
        String class_name = obj->IsJSObject()
            ? JSObject::cast(obj)->class_name()
            : ReadOnlyRoots(isolate_).empty_string();
        msg << "suspect-read" << kNext << class_name << kNext << name;
        msg.WriteToLogFile();
    }

    namespace {
        void AppendFunctionMessage(Log::MessageBuilder& msg, const char* reason,
            int script_id, double time_delta, int start_position,
            int end_position, base::ElapsedTimer* timer)
        {
            msg << "function" << Logger::kNext << reason << Logger::kNext << script_id
                << Logger::kNext << start_position << Logger::kNext << end_position
                << Logger::kNext << time_delta << Logger::kNext
                << timer->Elapsed().InMicroseconds() << Logger::kNext;
        }
    } // namespace

    void Logger::FunctionEvent(const char* reason, int script_id, double time_delta,
        int start_position, int end_position,
        String function_name)
    {
        if (!log_->IsEnabled() || !FLAG_log_function_events)
            return;
        Log::MessageBuilder msg(log_);
        AppendFunctionMessage(msg, reason, script_id, time_delta, start_position,
            end_position, &timer_);
        if (!function_name.is_null())
            msg << function_name;
        msg.WriteToLogFile();
    }

    void Logger::FunctionEvent(const char* reason, int script_id, double time_delta,
        int start_position, int end_position,
        const char* function_name,
        size_t function_name_length)
    {
        if (!log_->IsEnabled() || !FLAG_log_function_events)
            return;
        Log::MessageBuilder msg(log_);
        AppendFunctionMessage(msg, reason, script_id, time_delta, start_position,
            end_position, &timer_);
        if (function_name_length > 0) {
            msg.AppendString(function_name, function_name_length);
        }
        msg.WriteToLogFile();
    }

    void Logger::CompilationCacheEvent(const char* action, const char* cache_type,
        SharedFunctionInfo sfi)
    {
        if (!log_->IsEnabled() || !FLAG_log_function_events)
            return;
        Log::MessageBuilder msg(log_);
        int script_id = -1;
        if (sfi->script()->IsScript()) {
            script_id = Script::cast(sfi->script())->id();
        }
        msg << "compilation-cache" << Logger::kNext << action << Logger::kNext
            << cache_type << Logger::kNext << script_id << Logger::kNext
            << sfi->StartPosition() << Logger::kNext << sfi->EndPosition()
            << Logger::kNext << timer_.Elapsed().InMicroseconds();
        msg.WriteToLogFile();
    }

    void Logger::ScriptEvent(ScriptEventType type, int script_id)
    {
        if (!log_->IsEnabled() || !FLAG_log_function_events)
            return;
        Log::MessageBuilder msg(log_);
        msg << "script" << Logger::kNext;
        switch (type) {
        case ScriptEventType::kReserveId:
            msg << "reserve-id";
            break;
        case ScriptEventType::kCreate:
            msg << "create";
            break;
        case ScriptEventType::kDeserialize:
            msg << "deserialize";
            break;
        case ScriptEventType::kBackgroundCompile:
            msg << "background-compile";
            break;
        case ScriptEventType::kStreamingCompile:
            msg << "streaming-compile";
            break;
        }
        msg << Logger::kNext << script_id << Logger::kNext
            << timer_.Elapsed().InMicroseconds();
        msg.WriteToLogFile();
    }

    void Logger::ScriptDetails(Script script)
    {
        if (!log_->IsEnabled() || !FLAG_log_function_events)
            return;
        {
            Log::MessageBuilder msg(log_);
            msg << "script-details" << Logger::kNext << script->id() << Logger::kNext;
            if (script->name()->IsString()) {
                msg << String::cast(script->name());
            }
            msg << Logger::kNext << script->line_offset() << Logger::kNext
                << script->column_offset() << Logger::kNext;
            if (script->source_mapping_url()->IsString()) {
                msg << String::cast(script->source_mapping_url());
            }
            msg.WriteToLogFile();
        }
        EnsureLogScriptSource(script);
    }

    bool Logger::EnsureLogScriptSource(Script script)
    {
        if (!log_->IsEnabled())
            return false;
        Log::MessageBuilder msg(log_);
        // Make sure the script is written to the log file.
        int script_id = script->id();
        if (logged_source_code_.find(script_id) != logged_source_code_.end()) {
            return true;
        }
        // This script has not been logged yet.
        logged_source_code_.insert(script_id);
        Object source_object = script->source();
        if (!source_object->IsString())
            return false;
        String source_code = String::cast(source_object);
        msg << "script-source" << kNext << script_id << kNext;

        // Log the script name.
        if (script->name()->IsString()) {
            msg << String::cast(script->name()) << kNext;
        } else {
            msg << "<unknown>" << kNext;
        }

        // Log the source code.
        msg << source_code;
        msg.WriteToLogFile();
        return true;
    }

    void Logger::RuntimeCallTimerEvent()
    {
        RuntimeCallStats* stats = isolate_->counters()->runtime_call_stats();
        RuntimeCallCounter* counter = stats->current_counter();
        if (counter == nullptr)
            return;
        Log::MessageBuilder msg(log_);
        msg << "active-runtime-timer" << kNext << counter->name();
        msg.WriteToLogFile();
    }

    void Logger::TickEvent(v8::TickSample* sample, bool overflow)
    {
        if (!log_->IsEnabled() || !FLAG_prof_cpp)
            return;
        if (V8_UNLIKELY(TracingFlags::runtime_stats.load(std::memory_order_relaxed) == v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE)) {
            RuntimeCallTimerEvent();
        }
        Log::MessageBuilder msg(log_);
        msg << kLogEventsNames[CodeEventListener::TICK_EVENT] << kNext
            << reinterpret_cast<void*>(sample->pc) << kNext
            << timer_.Elapsed().InMicroseconds();
        if (sample->has_external_callback) {
            msg << kNext << 1 << kNext
                << reinterpret_cast<void*>(sample->external_callback_entry);
        } else {
            msg << kNext << 0 << kNext << reinterpret_cast<void*>(sample->tos);
        }
        msg << kNext << static_cast<int>(sample->state);
        if (overflow)
            msg << kNext << "overflow";
        for (unsigned i = 0; i < sample->frames_count; ++i) {
            msg << kNext << reinterpret_cast<void*>(sample->stack[i]);
        }
        msg.WriteToLogFile();
    }

    void Logger::ICEvent(const char* type, bool keyed, Map map, Object key,
        char old_state, char new_state, const char* modifier,
        const char* slow_stub_reason)
    {
        if (!log_->IsEnabled() || !FLAG_trace_ic)
            return;
        Log::MessageBuilder msg(log_);
        if (keyed)
            msg << "Keyed";
        int line;
        int column;
        Address pc = isolate_->GetAbstractPC(&line, &column);
        msg << type << kNext << reinterpret_cast<void*>(pc) << kNext << line << kNext
            << column << kNext << old_state << kNext << new_state << kNext
            << AsHex::Address(map.ptr()) << kNext;
        if (key->IsSmi()) {
            msg << Smi::ToInt(key);
        } else if (key->IsNumber()) {
            msg << key->Number();
        } else if (key->IsName()) {
            msg << Name::cast(key);
        }
        msg << kNext << modifier << kNext;
        if (slow_stub_reason != nullptr) {
            msg << slow_stub_reason;
        }
        msg.WriteToLogFile();
    }

    void Logger::MapEvent(const char* type, Map from, Map to, const char* reason,
        HeapObject name_or_sfi)
    {
        DisallowHeapAllocation no_gc;
        if (!log_->IsEnabled() || !FLAG_trace_maps)
            return;
        if (!to.is_null())
            MapDetails(to);
        int line = -1;
        int column = -1;
        Address pc = 0;

        if (!isolate_->bootstrapper()->IsActive()) {
            pc = isolate_->GetAbstractPC(&line, &column);
        }
        Log::MessageBuilder msg(log_);
        msg << "map" << kNext << type << kNext << timer_.Elapsed().InMicroseconds()
            << kNext << AsHex::Address(from.ptr()) << kNext
            << AsHex::Address(to.ptr()) << kNext << AsHex::Address(pc) << kNext
            << line << kNext << column << kNext << reason << kNext;

        if (!name_or_sfi.is_null()) {
            if (name_or_sfi->IsName()) {
                msg << Name::cast(name_or_sfi);
            } else if (name_or_sfi->IsSharedFunctionInfo()) {
                SharedFunctionInfo sfi = SharedFunctionInfo::cast(name_or_sfi);
                msg << sfi->DebugName();
#if V8_SFI_HAS_UNIQUE_ID
                msg << " " << SharedFunctionInfoWithID::cast(sfi)->unique_id();
#endif // V8_SFI_HAS_UNIQUE_ID
            }
        }
        msg.WriteToLogFile();
    }

    void Logger::MapCreate(Map map)
    {
        if (!log_->IsEnabled() || !FLAG_trace_maps)
            return;
        DisallowHeapAllocation no_gc;
        Log::MessageBuilder msg(log_);
        msg << "map-create" << kNext << timer_.Elapsed().InMicroseconds() << kNext
            << AsHex::Address(map.ptr());
        msg.WriteToLogFile();
    }

    void Logger::MapDetails(Map map)
    {
        if (!log_->IsEnabled() || !FLAG_trace_maps)
            return;
        DisallowHeapAllocation no_gc;
        Log::MessageBuilder msg(log_);
        msg << "map-details" << kNext << timer_.Elapsed().InMicroseconds() << kNext
            << AsHex::Address(map.ptr()) << kNext;
        if (FLAG_trace_maps_details) {
            std::ostringstream buffer;
            map->PrintMapDetails(buffer);
            msg << buffer.str().c_str();
        }
        msg.WriteToLogFile();
    }

    static void AddFunctionAndCode(SharedFunctionInfo sfi, AbstractCode code_object,
        Handle<SharedFunctionInfo>* sfis,
        Handle<AbstractCode>* code_objects, int offset)
    {
        if (sfis != nullptr) {
            sfis[offset] = Handle<SharedFunctionInfo>(sfi, sfi->GetIsolate());
        }
        if (code_objects != nullptr) {
            code_objects[offset] = Handle<AbstractCode>(code_object, sfi->GetIsolate());
        }
    }

    static int EnumerateCompiledFunctions(Heap* heap,
        Handle<SharedFunctionInfo>* sfis,
        Handle<AbstractCode>* code_objects)
    {
        HeapIterator iterator(heap);
        DisallowHeapAllocation no_gc;
        int compiled_funcs_count = 0;

        // Iterate the heap to find shared function info objects and record
        // the unoptimized code for them.
        for (HeapObject obj = iterator.next(); !obj.is_null();
             obj = iterator.next()) {
            if (obj->IsSharedFunctionInfo()) {
                SharedFunctionInfo sfi = SharedFunctionInfo::cast(obj);
                if (sfi->is_compiled() && (!sfi->script()->IsScript() || Script::cast(sfi->script())->HasValidSource())) {
                    AddFunctionAndCode(sfi, AbstractCode::cast(sfi->abstract_code()), sfis,
                        code_objects, compiled_funcs_count);
                    ++compiled_funcs_count;
                }
            } else if (obj->IsJSFunction()) {
                // Given that we no longer iterate over all optimized JSFunctions, we need
                // to take care of this here.
                JSFunction function = JSFunction::cast(obj);
                SharedFunctionInfo sfi = SharedFunctionInfo::cast(function->shared());
                Object maybe_script = sfi->script();
                if (maybe_script->IsScript() && !Script::cast(maybe_script)->HasValidSource()) {
                    continue;
                }
                // TODO(jarin) This leaves out deoptimized code that might still be on the
                // stack. Also note that we will not log optimized code objects that are
                // only on a type feedback vector. We should make this mroe precise.
                if (function->IsOptimized()) {
                    AddFunctionAndCode(sfi, AbstractCode::cast(function->code()), sfis,
                        code_objects, compiled_funcs_count);
                    ++compiled_funcs_count;
                }
            }
        }
        return compiled_funcs_count;
    }

    static int EnumerateWasmModuleObjects(
        Heap* heap, Handle<WasmModuleObject>* module_objects)
    {
        HeapIterator iterator(heap);
        DisallowHeapAllocation no_gc;
        int module_objects_count = 0;

        for (HeapObject obj = iterator.next(); !obj.is_null();
             obj = iterator.next()) {
            if (obj->IsWasmModuleObject()) {
                WasmModuleObject module = WasmModuleObject::cast(obj);
                if (module_objects != nullptr) {
                    module_objects[module_objects_count] = handle(module, Isolate::FromHeap(heap));
                }
                module_objects_count++;
            }
        }
        return module_objects_count;
    }

    void Logger::LogCodeObject(Object object)
    {
        existing_code_logger_.LogCodeObject(object);
    }

    void Logger::LogCodeObjects() { existing_code_logger_.LogCodeObjects(); }

    void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
        Handle<AbstractCode> code)
    {
        existing_code_logger_.LogExistingFunction(shared, code);
    }

    void Logger::LogCompiledFunctions()
    {
        existing_code_logger_.LogCompiledFunctions();
    }

    void Logger::LogAccessorCallbacks()
    {
        Heap* heap = isolate_->heap();
        HeapIterator iterator(heap);
        DisallowHeapAllocation no_gc;
        for (HeapObject obj = iterator.next(); !obj.is_null();
             obj = iterator.next()) {
            if (!obj->IsAccessorInfo())
                continue;
            AccessorInfo ai = AccessorInfo::cast(obj);
            if (!ai->name()->IsName())
                continue;
            Address getter_entry = v8::ToCData<Address>(ai->getter());
            Name name = Name::cast(ai->name());
            if (getter_entry != 0) {
#if USES_FUNCTION_DESCRIPTORS
                getter_entry = *FUNCTION_ENTRYPOINT_ADDRESS(getter_entry);
#endif
                PROFILE(isolate_, GetterCallbackEvent(name, getter_entry));
            }
            Address setter_entry = v8::ToCData<Address>(ai->setter());
            if (setter_entry != 0) {
#if USES_FUNCTION_DESCRIPTORS
                setter_entry = *FUNCTION_ENTRYPOINT_ADDRESS(setter_entry);
#endif
                PROFILE(isolate_, SetterCallbackEvent(name, setter_entry));
            }
        }
    }

    void Logger::LogAllMaps()
    {
        DisallowHeapAllocation no_gc;
        Heap* heap = isolate_->heap();
        HeapIterator iterator(heap);
        for (HeapObject obj = iterator.next(); !obj.is_null();
             obj = iterator.next()) {
            if (!obj->IsMap())
                continue;
            Map map = Map::cast(obj);
            MapCreate(map);
            MapDetails(map);
        }
    }

    static void AddIsolateIdIfNeeded(std::ostream& os, // NOLINT
        Isolate* isolate)
    {
        if (FLAG_logfile_per_isolate) {
            os << "isolate-" << isolate << "-" << base::OS::GetCurrentProcessId()
               << "-";
        }
    }

    static void PrepareLogFileName(std::ostream& os, // NOLINT
        Isolate* isolate, const char* file_name)
    {
        int dir_separator_count = 0;
        for (const char* p = file_name; *p; p++) {
            if (base::OS::isDirectorySeparator(*p))
                dir_separator_count++;
        }

        for (const char* p = file_name; *p; p++) {
            if (dir_separator_count == 0) {
                AddIsolateIdIfNeeded(os, isolate);
                dir_separator_count--;
            }
            if (*p == '%') {
                p++;
                switch (*p) {
                case '\0':
                    // If there's a % at the end of the string we back up
                    // one character so we can escape the loop properly.
                    p--;
                    break;
                case 'p':
                    os << base::OS::GetCurrentProcessId();
                    break;
                case 't':
                    // %t expands to the current time in milliseconds.
                    os << static_cast<int64_t>(
                        V8::GetCurrentPlatform()->CurrentClockTimeMillis());
                    break;
                case '%':
                    // %% expands (contracts really) to %.
                    os << '%';
                    break;
                default:
                    // All other %'s expand to themselves.
                    os << '%' << *p;
                    break;
                }
            } else {
                if (base::OS::isDirectorySeparator(*p))
                    dir_separator_count--;
                os << *p;
            }
        }
    }

    bool Logger::SetUp(Isolate* isolate)
    {
        // Tests and EnsureInitialize() can call this twice in a row. It's harmless.
        if (is_initialized_)
            return true;
        is_initialized_ = true;

        std::ostringstream log_file_name;
        std::ostringstream source_log_file_name;
        PrepareLogFileName(log_file_name, isolate, FLAG_logfile);
        log_ = new Log(this, log_file_name.str().c_str());

        if (FLAG_perf_basic_prof) {
            perf_basic_logger_.reset(new PerfBasicLogger(isolate));
            AddCodeEventListener(perf_basic_logger_.get());
        }

        if (FLAG_perf_prof) {
            perf_jit_logger_.reset(new PerfJitLogger(isolate));
            AddCodeEventListener(perf_jit_logger_.get());
        }

        if (FLAG_ll_prof) {
            ll_logger_.reset(new LowLevelLogger(isolate, log_file_name.str().c_str()));
            AddCodeEventListener(ll_logger_.get());
        }

        ticker_.reset(new Ticker(isolate, FLAG_prof_sampling_interval));

        if (Log::InitLogAtStart()) {
            is_logging_ = true;
        }

        timer_.Start();

        if (FLAG_prof_cpp) {
            profiler_.reset(new Profiler(isolate));
            is_logging_ = true;
            profiler_->Engage();
        }

        if (is_logging_) {
            AddCodeEventListener(this);
        }

        return true;
    }

    void Logger::SetCodeEventHandler(uint32_t options,
        JitCodeEventHandler event_handler)
    {
        if (jit_logger_) {
            RemoveCodeEventListener(jit_logger_.get());
            jit_logger_.reset();
        }

        if (event_handler) {
            if (isolate_->wasm_engine() != nullptr) {
                isolate_->wasm_engine()->EnableCodeLogging(isolate_);
            }
            jit_logger_.reset(new JitLogger(isolate_, event_handler));
            AddCodeEventListener(jit_logger_.get());
            if (options & kJitCodeEventEnumExisting) {
                HandleScope scope(isolate_);
                LogCodeObjects();
                LogCompiledFunctions();
            }
        }
    }

    sampler::Sampler* Logger::sampler() { return ticker_.get(); }

    void Logger::StopProfilerThread()
    {
        if (profiler_ != nullptr) {
            profiler_->Disengage();
            profiler_.reset();
        }
    }

    FILE* Logger::TearDown()
    {
        if (!is_initialized_)
            return nullptr;
        is_initialized_ = false;

        // Stop the profiler thread before closing the file.
        StopProfilerThread();

        ticker_.reset();

        if (perf_basic_logger_) {
            RemoveCodeEventListener(perf_basic_logger_.get());
            perf_basic_logger_.reset();
        }

        if (perf_jit_logger_) {
            RemoveCodeEventListener(perf_jit_logger_.get());
            perf_jit_logger_.reset();
        }

        if (ll_logger_) {
            RemoveCodeEventListener(ll_logger_.get());
            ll_logger_.reset();
        }

        if (jit_logger_) {
            RemoveCodeEventListener(jit_logger_.get());
            jit_logger_.reset();
        }

        return log_->Close();
    }

    void ExistingCodeLogger::LogCodeObject(Object object)
    {
        AbstractCode abstract_code = AbstractCode::cast(object);
        CodeEventListener::LogEventsAndTags tag = CodeEventListener::STUB_TAG;
        const char* description = "Unknown code from before profiling";
        switch (abstract_code->kind()) {
        case AbstractCode::INTERPRETED_FUNCTION:
        case AbstractCode::OPTIMIZED_FUNCTION:
            return; // We log this later using LogCompiledFunctions.
        case AbstractCode::BYTECODE_HANDLER:
            return; // We log it later by walking the dispatch table.
        case AbstractCode::STUB:
            description = "STUB code";
            tag = CodeEventListener::STUB_TAG;
            break;
        case AbstractCode::REGEXP:
            description = "Regular expression code";
            tag = CodeEventListener::REG_EXP_TAG;
            break;
        case AbstractCode::BUILTIN:
            if (Code::cast(object)->is_interpreter_trampoline_builtin() && Code::cast(object) != *BUILTIN_CODE(isolate_, InterpreterEntryTrampoline)) {
                return;
            }
            description = isolate_->builtins()->name(abstract_code->GetCode()->builtin_index());
            tag = CodeEventListener::BUILTIN_TAG;
            break;
        case AbstractCode::WASM_FUNCTION:
            description = "A Wasm function";
            tag = CodeEventListener::FUNCTION_TAG;
            break;
        case AbstractCode::JS_TO_WASM_FUNCTION:
            description = "A JavaScript to Wasm adapter";
            tag = CodeEventListener::STUB_TAG;
            break;
        case AbstractCode::WASM_TO_JS_FUNCTION:
            description = "A Wasm to JavaScript adapter";
            tag = CodeEventListener::STUB_TAG;
            break;
        case AbstractCode::WASM_INTERPRETER_ENTRY:
            description = "A Wasm to Interpreter adapter";
            tag = CodeEventListener::STUB_TAG;
            break;
        case AbstractCode::C_WASM_ENTRY:
            description = "A C to Wasm entry stub";
            tag = CodeEventListener::STUB_TAG;
            break;
        case AbstractCode::NUMBER_OF_KINDS:
            UNIMPLEMENTED();
        }
        CALL_CODE_EVENT_HANDLER(CodeCreateEvent(tag, abstract_code, description))
    }

    void ExistingCodeLogger::LogCodeObjects()
    {
        Heap* heap = isolate_->heap();
        HeapIterator iterator(heap);
        DisallowHeapAllocation no_gc;
        for (HeapObject obj = iterator.next(); !obj.is_null();
             obj = iterator.next()) {
            if (obj->IsCode())
                LogCodeObject(obj);
            if (obj->IsBytecodeArray())
                LogCodeObject(obj);
        }
    }

    void ExistingCodeLogger::LogCompiledFunctions()
    {
        Heap* heap = isolate_->heap();
        HandleScope scope(isolate_);
        const int compiled_funcs_count = EnumerateCompiledFunctions(heap, nullptr, nullptr);
        ScopedVector<Handle<SharedFunctionInfo>> sfis(compiled_funcs_count);
        ScopedVector<Handle<AbstractCode>> code_objects(compiled_funcs_count);
        EnumerateCompiledFunctions(heap, sfis.start(), code_objects.start());

        // During iteration, there can be heap allocation due to
        // GetScriptLineNumber call.
        for (int i = 0; i < compiled_funcs_count; ++i) {
            SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, sfis[i]);
            if (sfis[i]->function_data()->IsInterpreterData()) {
                LogExistingFunction(
                    sfis[i],
                    Handle<AbstractCode>(
                        AbstractCode::cast(sfis[i]->InterpreterTrampoline()), isolate_),
                    CodeEventListener::INTERPRETED_FUNCTION_TAG);
            }
            if (code_objects[i].is_identical_to(BUILTIN_CODE(isolate_, CompileLazy)))
                continue;
            LogExistingFunction(sfis[i], code_objects[i]);
        }

        const int wasm_module_objects_count = EnumerateWasmModuleObjects(heap, nullptr);
        std::unique_ptr<Handle<WasmModuleObject>[]> module_objects(
            new Handle<WasmModuleObject>[wasm_module_objects_count]);
        EnumerateWasmModuleObjects(heap, module_objects.get());
        for (int i = 0; i < wasm_module_objects_count; ++i) {
            module_objects[i]->native_module()->LogWasmCodes(isolate_);
        }
    }

    void ExistingCodeLogger::LogExistingFunction(
        Handle<SharedFunctionInfo> shared, Handle<AbstractCode> code,
        CodeEventListener::LogEventsAndTags tag)
    {
        if (shared->script()->IsScript()) {
            Handle<Script> script(Script::cast(shared->script()), isolate_);
            int line_num = Script::GetLineNumber(script, shared->StartPosition()) + 1;
            int column_num = Script::GetColumnNumber(script, shared->StartPosition()) + 1;
            if (script->name()->IsString()) {
                Handle<String> script_name(String::cast(script->name()), isolate_);
                if (line_num > 0) {
                    CALL_CODE_EVENT_HANDLER(
                        CodeCreateEvent(Logger::ToNativeByScript(tag, *script), *code,
                            *shared, *script_name, line_num, column_num))
                } else {
                    // Can't distinguish eval and script here, so always use Script.
                    CALL_CODE_EVENT_HANDLER(CodeCreateEvent(
                        Logger::ToNativeByScript(CodeEventListener::SCRIPT_TAG, *script),
                        *code, *shared, *script_name))
                }
            } else {
                CALL_CODE_EVENT_HANDLER(CodeCreateEvent(
                    Logger::ToNativeByScript(tag, *script), *code, *shared,
                    ReadOnlyRoots(isolate_).empty_string(), line_num, column_num))
            }
        } else if (shared->IsApiFunction()) {
            // API function.
            FunctionTemplateInfo fun_data = shared->get_api_func_data();
            Object raw_call_data = fun_data->call_code();
            if (!raw_call_data->IsUndefined(isolate_)) {
                CallHandlerInfo call_data = CallHandlerInfo::cast(raw_call_data);
                Object callback_obj = call_data->callback();
                Address entry_point = v8::ToCData<Address>(callback_obj);
#if USES_FUNCTION_DESCRIPTORS
                entry_point = *FUNCTION_ENTRYPOINT_ADDRESS(entry_point);
#endif
                CALL_CODE_EVENT_HANDLER(CallbackEvent(shared->DebugName(), entry_point))
            }
        }
    }

#undef CALL_CODE_EVENT_HANDLER

} // namespace internal
} // namespace v8
